import numpy as np


class SVM(object):
    def __init__(self, C=1.0):
        self._support_vectors = None
        self.C = C
        self.W = None
        self.b = None
        self.x = None
        self.y = None
        self.n = 0
        self.d = 0

    def __desicion_function(self, X):
        return X.dot(self.W) + self.b

    def __margin(self, X, y):
        return y * self.__desicion_function(X)

    def __cost(self, margin):
        return (1 / 2) * self.W.dot(self.W) + self.C * np.sum(np.maximum(0, 1 - margin))

    def fit(self, X, y, lr=1e-3, epochs=500):
        self.n, self.d = X.shape[0], X.shape[1]
        self.W = np.random.rand(self.d)
        self.b = np.random.rand()

        self.x = X
        self.y = y
        losses = []
        for i in range(epochs):
            margin = self.__margin(X, y)
            loss = self.__cost(margin)
            losses.append(loss)

            missclassified_pts_idx = np.where(margin < 1)[0]
            d_W = self.W - self.C * y[missclassified_pts_idx].dot(X[missclassified_pts_idx])
            self.W = self.W - lr * d_W

            d_gama = -self.C * np.sum(y[missclassified_pts_idx])
            self.b = self.b - lr * d_gama

            self._support_vectors = np.where(self.__margin(X, y) < 1)[0]

    def predict(self, X):
        return np.sign(self.__desicion_function(X))

    def score(self, X, y):
        P = self.predict(X)
        return np.mean(P == y)





